package be.kuleuven.cs.mop.app.controllers;


import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import be.kuleuven.cs.mop.app.System;
import be.kuleuven.cs.mop.domain.exceptions.BusinessRuleThreeViolationExceptionDeadlineExpired;
import be.kuleuven.cs.mop.domain.exceptions.BusinessRuleThreeViolationExceptionTaskScheduleDoesNotCoverCurrentTime;
import be.kuleuven.cs.mop.domain.exceptions.BusinessRuleTwoViolationExceptionDependantTasks;
import be.kuleuven.cs.mop.domain.exceptions.BusinessRuleTwoViolationExceptionTask;
import be.kuleuven.cs.mop.domain.exceptions.InvalidCurrentUserException;
import be.kuleuven.cs.mop.domain.exceptions.TaskManagerException;
import be.kuleuven.cs.mop.domain.model.Task;
import be.kuleuven.cs.mop.domain.model.TaskStatus;
import be.kuleuven.cs.mop.domain.model.TaskType;
import be.kuleuven.cs.mop.domain.model.User;
import be.kuleuven.cs.mop.domain.model.impl.Field;
import be.kuleuven.cs.mop.domain.model.impl.FocusWorkAlgorithmDeadline;
import be.kuleuven.cs.mop.domain.model.impl.FocusWorkAlgorithmDuration;
import be.kuleuven.cs.mop.domain.model.impl.FocusWorkAlgorithmTaskType;
import be.kuleuven.cs.mop.domain.model.impl.Schedule;
import be.kuleuven.cs.mop.domain.model.impl.TaskImpl;
import be.kuleuven.cs.mop.domain.model.impl.TaskTypeImpl;
import be.kuleuven.cs.mop.domain.model.impl.UserImpl;



/**
 * The {@link Controller} responsible for handling {@link Task} operations
 */
public class TaskController extends Controller {
	
	public TaskController(final System system) {
		super(system);
	}
	
	/**
	 * Checks whether the current user is the owner of the given task
	 * @param task - The given task
	 * @throws InvalidCurrentUserException USER_NOT_OWNER_OF_TASK - The current user is not the owner of the given task
	 */
	private void checkCurrentUserIsOwnerOfTask(final Task task) throws InvalidCurrentUserException {
		if(!task.getUser().equals(getCurrentUser())){
			throw new InvalidCurrentUserException(InvalidCurrentUserException.USER_NOT_OWNER_OF_TASK);
		}
	}
	
	 /**
	 * Creates a new task in the system
	 * @param schedule - The schedule for the new task
	 * @param dependencies - The list of dependencies for the new task
	 * @param resources - The list of resources for the new task
	 * @param fields - The list of fields for the new task
	 * @param type - The task type for the new task
	 * 
	 * @return The created task
	 * 
	 * @throws TaskManagerException - {@link UserImpl#createTask}
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 */
	public Task createTask(final Schedule schedule,	final Set<Task> dependencies, final HashMap<String,Field> fields,
			final TaskType type) throws TaskManagerException, InvalidCurrentUserException {
		checkIfUserIsLoggedIn();
		final Set<TaskImpl>		dep	= new HashSet<TaskImpl>();
		// Transform interface object lists to implementation object lists
		if (dependencies != null)
			for (final Task t : dependencies)
				dep.add((TaskImpl)t);
		// Create the task for the current user
		return getCurrentUser().createTask(
				schedule, dep, fields, (TaskTypeImpl)type);
	}
	
	/**
	 * Returns a <code>Collection</code> of focus <code>Task</code>s
	 * for the current <code>User</code> based on deadline
	 * @param max - the maximum number of tasks to return
	 * @return the deadline-sorted tasks of the current user
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 */
	public Collection<Task> getFocusTasksDeadline(final int max) throws InvalidCurrentUserException{
		checkIfUserIsLoggedIn();
		return new ArrayList<Task>(
				new FocusWorkAlgorithmDeadline(getCurrentUser(), max).execute());
	}
	
	/**
	 * Returns a <code>Collection</code> of focus <code>Task</code>s
	 * for the current <code>User</code> based on duration
	 * @param min - lower bound for the duration of the wanted tasks
	 * @param max - upper bound for the duration of the wanted tasks
	 * @return the duration-sorted tasks of the current user
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 */
	public Collection<Task> getFocusTasksDuration(final int min, final int max) throws InvalidCurrentUserException{
		checkIfUserIsLoggedIn();
		return new ArrayList<Task>(new FocusWorkAlgorithmDuration(
				getCurrentUser(), min, max).execute());
	}
	
	/**
	 * Returns a <code>Collection</code> of focus <code>Task</code>s
	 * for the current <code>User</code> based on {@link TaskType}
	 * @param min - lower bound for the duration of the wanted tasks
	 * @param max - upper bound for the duration of the wanted tasks
	 * @return the tasktype-sorted tasks of the current user
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 */
	public Collection<Task> getFocusTasksTaskTypes(TaskType type) throws InvalidCurrentUserException,TaskManagerException{
		checkIfUserIsLoggedIn();
		return new ArrayList<Task>(new FocusWorkAlgorithmTaskType(getCurrentUser(), (TaskTypeImpl)type).execute());
	}
	
	/**
	 * Returns a <code>List</code> of all {@link Task}s for the current {@link User}
	 * 
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 */
	public List<Task> getUserTasks() throws InvalidCurrentUserException {
		checkIfUserIsLoggedIn();
		return new ArrayList<Task>(getCurrentUser().getTasks());
	}
	
	/**
	 * Returns a <code>List</code> of all unfinished {@link Task}s
	 * 	for the current logged in {@link User}
	 * 
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN
	 * 				- {@link #checkIfUserIsLoggedIn()} 
	 */
	public List<Task> getUserTasksUnfinished() throws InvalidCurrentUserException {
		checkIfUserIsLoggedIn();
		return new ArrayList<Task>(getCurrentUser().getTasksUnfinished());
	}
	
	/**
	 * Returns a <code>List</code> of all {@link Task}s in the system
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()} 
	 */
	public List<Task> getTasks() throws InvalidCurrentUserException {
		checkIfUserIsLoggedIn();
		return new ArrayList<Task>(getWorld().getTasks());
	}
	
	/**
	 * Returns a <code>List</code> of all {@link TaskType}s in the system
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()} 
	 */
	public List<TaskType> getTaskTypes() throws InvalidCurrentUserException {
		checkIfUserIsLoggedIn();
		return new ArrayList<TaskType>(getWorld().getTaskTypes());
	}
	
	/**
	 * Modifies the details of the specified {@link Task}
	 * @param task - the {@link Task} to modify
	 * @param schedule - the new schedule
	 * @param dependencies - the new list of {@link Task}s where the task depends on
	 * @param fields - the modified fields for this task
	 * 
	 * @throws TaskManagerException - {@link TaskImpl#modify} 
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 * @throws InvalidCurrentUserException USER_NOT_OWNER_OF_TASK - {@link #checkCurrentUserIsOwnerOfTask(Task)}
	 * @throws BusinessRuleTwoViolationExceptionTask - {@link TaskImpl#modify} 
	 */
	public void modifyTask(final Task task, final Schedule schedule, final Set<Task> dependencies, final HashMap<String, Field> fields)
	throws TaskManagerException, InvalidCurrentUserException, BusinessRuleTwoViolationExceptionTask {
		checkCurrentUserIsOwnerOfTask(task);
		final Set<TaskImpl>		dep	= new HashSet<TaskImpl>();
		// Transform interface object lists to implementation object lists
		if (dependencies != null)
			for (final Task t : dependencies)
				dep.add((TaskImpl)t);
		
		 ((TaskImpl)task).modify(fields, schedule, dep);
	}
	
	/**
	 * Remove a Task.
	 * @param task - a <code>Task</code>
	 * 
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 * @throws InvalidCurrentUserException USER_NOT_OWNER_OF_TASK - {@link #checkCurrentUserIsOwnerOfTask(Task)}
	 */
	public void removeTask(final Task task) throws TaskManagerException, InvalidCurrentUserException {
		checkCurrentUserIsOwnerOfTask(task);
		((TaskImpl)task).remove();
	}
	
	/**
	 * Remove a Task recursively.
	 * @param task - a <code>Task</code>
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 * @throws InvalidCurrentUserException USER_NOT_OWNER_OF_TASK - {@link #checkCurrentUserIsOwnerOfTask(Task)}
	 */
	public void removeTaskRecursively(final Task task) throws TaskManagerException, InvalidCurrentUserException {
		checkCurrentUserIsOwnerOfTask(task);
		((TaskImpl)task).removeRecursive();
	}
	
	/**
	 * Updates the status of the specified {@link Task}
	 * @param task - a <code>Task</code>
	 * @param status - the new {@link TaskStatus}
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 * @throws InvalidCurrentUserException USER_NOT_OWNER_OF_TASK - {@link #checkCurrentUserIsOwnerOfTask(Task)}
	 * @throws BusinessRuleThreeViolationExceptionDeadlineExpired 
	 * @throws BusinessRuleThreeViolationExceptionTaskScheduleDoesNotCoverCurrentTime 
	 * @throws BusinessRuleTwoViolationExceptionTask 
	 * @throws BusinessRuleTwoViolationExceptionDependantTasks 
	 * 
	 */
	public void updateTaskStatus(final Task task, final TaskStatus status) throws TaskManagerException, InvalidCurrentUserException, BusinessRuleTwoViolationExceptionDependantTasks, BusinessRuleTwoViolationExceptionTask, BusinessRuleThreeViolationExceptionTaskScheduleDoesNotCoverCurrentTime, BusinessRuleThreeViolationExceptionDeadlineExpired{
		checkCurrentUserIsOwnerOfTask(task);
		((TaskImpl)task).updateTaskStatus(status);
	}
	
	/**
	 * Updates the status of the specified {@link Task} recursively
	 * @param task - a <code>Task</code>
	 * @param status - the new {@link TaskStatus}
	 * @throws TaskManagerException BUSINESS_RULE_VIOLATION_CAN_NOT_BE_HANDLED_RECURSIVELY if the violation caused by updating this task status isn't a violation caused by the task dependants' status(es)
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 * @throws InvalidCurrentUserException USER_NOT_OWNER_OF_TASK - {@link #checkCurrentUserIsOwnerOfTask(Task)}
	 */
	public void updateTaskStatusRecursively(final Task task, final TaskStatus status) throws TaskManagerException, InvalidCurrentUserException{
		checkCurrentUserIsOwnerOfTask(task);
		((TaskImpl)task).updateTaskStatusRecursively(status);
	}
}